package com.platform.boot.security.filter;

import com.platform.boot.security.ReactiveSecurityHelper;
import com.platform.boot.security.ReactiveSecurityUserHolder;
import com.platform.boot.security.SecurityUserDetails;
import com.platform.boot.security.SecurityUserManager;
import org.springframework.core.Ordered;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

/**
 * @author Alex bob(<a href="https://github.com/vnobo">https://github.com/vnobo</a>)
 */
public record BeforeSecurityFilter(SecurityUserManager securityManager) implements WebFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return Mono.defer(() -> {
            Mono<SecurityUserDetails> securityDetailsToken = securityDetailsToken(exchange);
            exchange.getAttributes().put(ReactiveSecurityHelper.SECURITY_TOKEN_CONTEXT, securityDetailsToken);
            return chain.filter(exchange).contextWrite(ReactiveSecurityUserHolder
                    .withSecurityDetailsContext(securityDetailsToken));
        });
    }

    private Mono<SecurityUserDetails> securityDetailsToken(ServerWebExchange exchange) {
        return this.loadToken(exchange).switchIfEmpty(generateToken(exchange));
    }

    private Mono<SecurityUserDetails> loadToken(ServerWebExchange exchange) {
        return exchange.getSession().filter((session) -> session.getAttributes()
                        .containsKey(ReactiveSecurityHelper.SECURITY_TOKEN_CONTEXT))
                .mapNotNull((session) -> session.getAttribute(ReactiveSecurityHelper.SECURITY_TOKEN_CONTEXT));
    }

    private Mono<SecurityUserDetails> generateToken(ServerWebExchange exchange) {
        return ReactiveSecurityContextHolder.getContext()
                .flatMap(securityContext -> securityManager.loadSecurity(securityContext.getAuthentication().getName()))
                .delayUntil((securityDetails) -> ReactiveSecurityHelper.saveToken(exchange, securityDetails));
    }

    @Override
    public int getOrder() {
        return SecurityWebFiltersOrder.AUTHORIZATION.getOrder() + 100;
    }
}